use super::TaskPool;
use core::alloc::Layout;
use core::ptr::NonNull;
use hicollections::{list, List, ListNode};
use hierr::Result;
use hipool::{Boxed, MemPool, NullAlloc};

struct CacheNode {
    node: ListNode,
    pool: *mut MemPool,
    layout: Layout,
}

impl CacheNode {
    fn new(pool: TaskPool) -> Boxed<'static, Self, MemPool> {
        let pool = MemPool::reset_boxed(pool);
        let (pool, layout, _) = pool.leak();
        let ptr: *mut MemPool = pool;
        Boxed::new_in(
            pool,
            Self {
                node: ListNode::new(),
                pool: ptr,
                layout,
            },
        )
        .unwrap()
    }

    // self不再可用
    unsafe fn release(&self) -> TaskPool {
        let pool = Boxed::from_with(NonNull::new_unchecked(self.pool), self.layout, &NullAlloc);
        MemPool::reset_boxed(pool)
    }
}

pub(crate) struct PoolCache {
    caches: List<CacheNode>,
    size: usize,
    max_size: usize,
}

impl PoolCache {
    pub fn new(max_size: usize) -> Self {
        Self {
            caches: list!(CacheNode, node),
            size: 0,
            max_size,
        }
    }

    pub fn push(&mut self, pool: TaskPool) {
        if self.max_size == 0 {
            return;
        }
        let node = CacheNode::new(pool).leak().0;
        unsafe { self.caches.add_head(node) };
        if self.size < self.max_size {
            self.size += 1;
        } else {
            let cache = self.caches.last().unwrap();
            unsafe { self.caches.del(cache) };
            let _ = unsafe { cache.release() };
        }
    }

    pub fn pop(&mut self) -> Result<TaskPool> {
        if let Some(cache) = self.caches.first() {
            unsafe { self.caches.del(cache) };
            self.size -= 1;
            Ok(unsafe { cache.release() })
        } else {
            MemPool::new_boxed(0)
        }
    }
}

impl Drop for PoolCache {
    fn drop(&mut self) {
        while let Some(cache) = self.caches.first() {
            unsafe { self.caches.del(cache) };
            unsafe { cache.release() };
        }
    }
}
